﻿using LSFTP.model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace LSFTP
{
    public class FTPClient : IFTP
    {
        #region 字段和属性
        private string host = string.Empty;
        private string userId = string.Empty;
        private string password = string.Empty;
        private int port = 21;
        IWebProxy proxy = null;
        private bool enableSsl = false;
        private bool usePassive = true;
        private bool useBinary = true;
        private string remotePath = "/";

        /// <summary>
        /// 主机
        /// </summary>
        public string Host { get { return this.host ?? string.Empty; } }

        /// <summary>
        /// 登录用户名
        /// </summary>
        public string UserId { get { return this.userId; } }

        /// <summary>
        /// 密码
        /// </summary>
        public string Password { get { return this.password; } }

        /// <summary>
        /// 端口
        /// </summary>
        public int Port { get { return this.port; } set { this.port = value; } }

        /// <summary>
        /// 代理
        /// </summary>
        public IWebProxy Proxy { get { return this.proxy; } set { this.proxy = value; } }

        /// <summary>
        /// 设置是否允许Ssl
        /// </summary>
        public bool EnableSsl { get { return enableSsl; } }

        /// <summary>
        /// 是否使用被动模式
        /// </summary>
        public bool UsePassive { get { return this.usePassive; } set { this.usePassive = value; } }

        /// <summary>
        /// 是否二进制方式
        /// </summary>
        public bool UseBinary { get { return this.useBinary; } set { this.useBinary = value; } }

        /// <summary>
        /// 远端路径
        /// <para>
        ///     返回FTP服务器上的当前路径(可以是 / 或 /a/../ 的形式)
        /// </para>
        /// </summary>
        public string RemotePath
        {
            get
            {
                return remotePath;
            }
            set
            {
                string result = "/";
                if (!string.IsNullOrEmpty(value) && value != "/")
                {
                    result = "/" + value.TrimStart('/').TrimEnd('/') + "/";
                }
                this.remotePath = result;
            }
        }
        #endregion

        #region 构造函数
        /// <summary>
        /// 创建FTP工具
        /// <para>
        /// 默认不使用SSL,使用二进制传输方式,使用被动模式
        /// </para>
        /// </summary>
        /// <param name="host">主机名称</param>
        /// <param name="userId">用户名</param>
        /// <param name="password">密码</param>
        public FTPClient(string host, string userId, string password) : this(host, 21, userId, password, null, false, true, true)
        {
        }

        /// <summary>
        /// 创建FTP工具
        /// <para>
        /// 默认不使用SSL,使用二进制传输方式,使用被动模式
        /// </para>
        /// </summary>
        /// <param name="host">主机名称</param>
        /// <param name="userId">用户名</param>
        /// <param name="password">密码</param>
        public FTPClient(string host, int port, string userId, string password) : this(host, port, userId, password, null, false, true, true)
        {
        }

        /// <summary>
        /// 创建FTP工具
        /// </summary>
        /// <param name="host">主机名称</param>
        /// <param name="userId">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="port">端口</param>
        /// <param name="enableSsl">允许Ssl</param>
        /// <param name="proxy">代理</param>
        /// <param name="useBinary">允许二进制</param>
        /// <param name="usePassive">允许被动模式</param>
        public FTPClient(string host, int port, string userId, string password, IWebProxy proxy, bool enableSsl, bool useBinary, bool usePassive)
        {
            this.userId = userId;
            this.password = password;
            if (host.ToLower().StartsWith("ftp://") || host.ToLower().StartsWith("sftp://") || host.ToLower().StartsWith("ftps://"))
            {
                this.host = host;
            }
            else
            {
                this.host = "ftp://" + host + ":" + port;
            }
            if (!this.host.Contains(":"))
                this.host = this.host + ":" + port;
            this.port = port;
            this.proxy = proxy;
            this.enableSsl = enableSsl;
            this.useBinary = useBinary;
            this.usePassive = usePassive;
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 创建一个FTP请求
        /// </summary>
        /// <param name="url">请求地址</param>
        /// <param name="method">请求方法</param>
        /// 
        /// <returns>FTP请求</returns>
        private FtpWebRequest CreateRequest(string url, string method)
        {
            //建立连接
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
            request.Credentials = new NetworkCredential(this.userId, this.password);
            request.Proxy = this.proxy;
            request.KeepAlive = false;//命令执行完毕之后关闭连接
            request.UseBinary = useBinary;
            request.UsePassive = usePassive;
            request.EnableSsl = enableSsl;
            request.Method = method;
            return request;
        }

        /// <summary>
        /// 文件夹如果不存在则创建,多层创建
        /// </summary>
        /// <param name="dirName">文件夹路径名称</param>
        /// <returns>返回一个值,指示是否创建成功</returns>
        private bool CheckAndMakeDirectory(string dirPath)
        {
            bool result = true;

            if (!string.IsNullOrEmpty(dirPath))
            {
                string[] folderPaths = dirPath.Split('/');
                string rebuilt = string.Empty;
                foreach (string pth in folderPaths)
                {
                    if (string.IsNullOrEmpty(pth))
                        continue;

                    //先获服务器目录列表
                    List<string> dirList = GetDirectoryList(rebuilt);

                    rebuilt += pth + "/";
                    //判断是否存在
                    if (!dirList.Contains(pth))
                    {
                        result = MakeDirectory(rebuilt);
                        if (!result) return result;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 获取详细列表 从FTP服务器上获取文件和文件夹列表
        /// </summary>
        /// <returns></returns>
        private List<string> GetFileDetails(string remoteFolder = "")
        {
            if (string.IsNullOrEmpty(remoteFolder))
                remoteFolder = RemotePath;

            List<string> result = new List<string>();
            //建立连接
            string url = Host.TrimEnd('/') + "/" + remoteFolder.TrimStart('/');
            FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.ListDirectoryDetails);
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8);//中文文件名
                string line = reader.ReadLine();
                while (line != null)
                {
                    result.Add(line);
                    line = reader.ReadLine();
                }
                response.Close();
            }
            return result;
        }

        #endregion

        #region 接口方法实现
        /// <summary>
        /// 连接FTP
        /// </summary>
        /// <returns>true成功</returns>
        public bool Connect()
        {
            return true;
        }

        /// <summary>
        /// 断开FTP
        /// </summary> 
        public void Disconnect()
        {
            
        }

        /// <summary>
        /// 获取连接状态
        /// </summary>
        /// <returns></returns>
        public bool GetConnectStatus()
        {
            return false;
        }

        /// <summary>
        /// FTP上传文件
        /// </summary>
        /// <param name="localPath">本地路径</param>
        /// <param name="remotePath">远程路径</param>
        /// <returns>返回一个值，指示是否上传成功</returns>
        public bool Upload(string localPath, string remotePath)
        {
            bool result = false;
            if (!File.Exists(localPath))
                return result;

            FileInfo localFile = new FileInfo(localPath);
            if (localFile.Exists)
            {
                //判断服务文件路径是否存在
                string serverPath = remotePath.Replace(@"\", "/");
                string directoryPath = string.Empty;
                if (serverPath.LastIndexOf('/') > 0)
                    directoryPath = serverPath.Substring(0, serverPath.LastIndexOf('/'));
                result = CheckAndMakeDirectory(directoryPath);
                if (!result) return result;

                string url = Host.TrimEnd('/') + RemotePath + serverPath;
                FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.UploadFile);
                //上传数据
                using (Stream rs = request.GetRequestStream())
                {
                    using (FileStream fs = localFile.OpenRead())
                    {
                        byte[] buffer = new byte[4096];//4K
                        int count = fs.Read(buffer, 0, buffer.Length);
                        while (count > 0)
                        {
                            rs.Write(buffer, 0, count);
                            count = fs.Read(buffer, 0, buffer.Length);
                        }
                        fs.Close();
                        result = true;
                    }
                    rs.Close();
                }

                return result;
            }
            throw new Exception(string.Format("本地文件不存在,文件路径:{0}", localFile.FullName));
        }

        /// <summary>
        /// 下载FTP文件
        /// </summary>
        /// <param name="remoteFile">远程路径</param>
        /// <param name="localPath">本地路径</param>
        /// <returns>返回一个值，指示是否下载成功</returns>
        public bool Download(string remoteFile, string localPath)
        {
            bool result = false;
            string filePath = localPath.Substring(0, localPath.LastIndexOf('\\'));
            if (!Directory.Exists(filePath))
                Directory.CreateDirectory(filePath);
            using (FileStream fs = new FileStream(localPath, FileMode.OpenOrCreate)) //创建或打开本地文件
            {
                //建立连接
                string url = Host.TrimEnd('/') + RemotePath + remoteFile;
                FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.DownloadFile);
                request.ContentOffset = fs.Length;
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    fs.Position = fs.Length;
                    byte[] buffer = new byte[4096];//4K
                    int count = response.GetResponseStream().Read(buffer, 0, buffer.Length);
                    while (count > 0)
                    {
                        fs.Write(buffer, 0, count);
                        count = response.GetResponseStream().Read(buffer, 0, buffer.Length);
                    }
                    response.GetResponseStream().Close();
                }
                result = true;

                fs.Close();
            }
            return result;
        }

        /// <summary>
        /// 重命名FTP文件
        /// </summary>
        /// <param name="oldFilePath">旧远程路径</param>
        /// <param name="newFilePath">新远程路径</param>
        /// <returns>返回一个值，指示是否重命名成功</returns>
        public bool Rename(string oldFilePath, string newFilePath)
        {
            bool result = false;
            string oldFileName_ = oldFilePath.Replace(@"\", "/");
            string newFileName_ = newFilePath.Replace(@"\", "/");
            //建立连接
            string url = Host.TrimEnd('/') + RemotePath + oldFileName_;
            FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.Rename);
            request.RenameTo = newFileName_.Substring(newFileName_.LastIndexOf("/") + 1);
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                result = true;
                response.Close();
            }
            return result;
        }

        /// <summary>
        /// 删除FTP文件
        /// </summary>
        /// <param name="remoteFile">远程文件路径</param>
        /// <returns>返回一个值，指示是否删除成功</returns>
        public bool DeleteFile(string remoteFile)
        {
            bool result = false;
            //建立连接
            string url = Host.TrimEnd('/') + RemotePath + remoteFile;
            FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.DeleteFile);
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                result = true;
                response.Close();
            }
            return result;
        }

        /// <summary>
        /// 删除文件夹
        /// </summary>
        /// <param name="dirName">远程文件夹路径</param>
        /// <returns>返回一个值，指示是否删除成功</returns>
        public bool DeleteDirectory(string dirName)
        {
            bool result = false;
            //建立连接
            string url = Host.TrimEnd('/') + RemotePath + dirName;
            FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.RemoveDirectory);
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                result = true;
                response.Close();
            }
            return result;
        }

        /// <summary>
        /// 获取FTP服务根目录文件列表
        /// </summary>
        /// <returns>返回文件名列表</returns>
        public List<string> GetFileList()
        {
            return GetFileList(string.Empty, "*.*");
        }

        /// <summary>
        /// 获取FTP文件列表
        /// </summary>
        /// <param name="remotePath"></param>
        /// <returns>返回文件名列表</returns>
        public List<string> GetFileList(string remotePath)
        {
            return GetFileList(remotePath, "*.*");
        }

        /// <summary>
        /// 获取固定后缀的FTP文件列表
        /// </summary>
        /// <param name="remotePath">远程文件夹路径</param>
        /// <param name="fileSuffix">文件后缀</param>
        /// <returns>返回文件名列表</returns>
        public List<string> GetFileList(string remotePath, string fileSuffix)
        {
            string serverPath = RemotePath;
            if (!string.IsNullOrEmpty(remotePath))
                serverPath = remotePath.Replace(@"\", "/");

            List<string> result = new List<string>();
            List<string> list = GetFileDetails(serverPath);
            string suffix_ = string.Empty;
            if (!string.IsNullOrEmpty(fileSuffix) && fileSuffix.Contains("."))
                suffix_ = fileSuffix.Substring(fileSuffix.LastIndexOf('.'));
            foreach (string info in list)
            {
                string auth = info.Substring(0, 10);
                string subFolder = info.Substring(49).TrimStart(' ');
                //会有两种格式的详细信息返回
                //一种包含<DIR>
                //一种第一个字符串是drwxerwxx这样的权限操作符号
                //drwxr-xr-x 1 ftp ftp              0 Feb 04 17:44 NewFolder
                //判断是否是文件夹
                if (info.Trim().Substring(0, 3).ToUpper() == "DRW" || info.Trim().Contains("<DIR>"))
                {
                    if (info.Trim().Contains("<DIR>"))
                        subFolder = info.Substring(39).Trim();

                    string subFolder_ = serverPath + "/" + subFolder;
                    List<string> rlts = GetFileList(subFolder_, fileSuffix);
                    foreach (string subPath in rlts)
                        result.Add(subPath);
                }
                else
                {
                    if (!string.IsNullOrEmpty(fileSuffix) && !".*".Equals(suffix_))
                    {
                        string gSuffix = string.Empty;
                        if (subFolder.Contains("."))
                            gSuffix = subFolder.Substring(subFolder.LastIndexOf('.'));

                        if (!suffix_.Equals(gSuffix))
                        {
                            continue;
                        }
                    }


                    if ("/".Equals(serverPath))
                    {
                        result.Add(serverPath + subFolder);
                    }
                    else
                    {
                        result.Add("/" + serverPath.TrimStart('/') + "/" + subFolder);
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 深层遍历文件夹，获取FTP文件列表(暂时未实现遍历)
        /// </summary>
        /// <param name="remotePath">远程文件夹路径</param>
        /// <param name="fileSuffix">文件后缀</param>
        /// <param name="deep">遍历获取</param>
        /// <returns>返回文件名列表</returns>
        public List<string> GetFileList(string remotePath, string fileSuffix, bool deep)
        {
            return GetFileList(remotePath, fileSuffix);
        }

        /// <summary>
        /// 获取服务目录列表
        /// </summary>
        /// <param name="remoteFolder">根目录名称</param>
        /// <returns>返回目录名列表</returns>
        public List<string> GetDirectoryList(string remoteFolder = "")
        {
            List<string> drectory = GetFileDetails(remoteFolder);
            List<string> dirList = new List<string>();
            if (drectory.Count > 0)
            {
                foreach (string str in drectory)
                {
                    if (str.Trim().Length == 0)
                        continue;
                    //会有两种格式的详细信息返回
                    //一种包含<DIR>
                    //一种第一个字符串是drwxerwxx这样的权限操作符号
                    //现在写代码包容两种格式的字符串
                    if (str.Trim().Contains("<DIR>"))
                    {
                        dirList.Add(str.Substring(39).Trim());
                    }
                    else
                    {
                        if (str.Trim().Substring(0, 3).ToUpper() == "DRW")
                        {
                            dirList.Add(str.Substring(49).Trim());
                        }
                    }
                }
            }
            return dirList;
        }

        /// <summary>
        /// 移动FTP文件(暂时未实现)
        /// </summary>
        /// <param name="oldRemotePath">旧远程文件路径</param>
        /// <param name="newRemotePath">新远程文件路径</param>
        /// <returns>返回一个值，指示是否移动成功</returns>
        public bool Move(string oldRemotePath, string newRemotePath)
        {
            return false;
        }

        /// <summary>
        /// 检查当前路径上是否存在某个文件
        /// </summary>
        /// <param name="remoteFile">要检查的文件名</param>
        /// <returns>返回一个值,指示要检查的文件是否存在</returns>
        public bool CheckFileExist(string remoteFile)
        {
            bool result = false;
            if (remoteFile != null && remoteFile.Trim().Length > 0)
            {
                remoteFile = remoteFile.Trim();
                List<string> files = GetFileList();
                if (files != null && files.Count > 0)
                {
                    foreach (string file in files)
                    {
                        if (file.ToLower() == remoteFile.ToLower())
                        {
                            result = true;
                            break;
                        }
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 在当前目录下创建文件夹
        /// </summary>
        /// <param name="dirName">文件夹名称</param>
        /// <returns>返回一个值,指示是否创建成功</returns>
        public bool MakeDirectory(string dirName)
        {
            bool result = false;
            //建立连接
            string url = Host.TrimEnd('/') + RemotePath + dirName;
            FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.MakeDirectory);
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                result = true;
                response.Close();
            }
            return result;
        }

        /// <summary>
        /// 在当前目录下创建文件夹
        /// </summary>
        /// <param name="path">远程路径</param>
        /// <param name="name">文件名</param>
        /// <returns>返回一个值,指示是否创建成功</returns>
        public bool MakeDirectory(string path, string name)
        {
            bool result = false;
            //建立连接
            string url = Host.TrimEnd('/') + path +"/"+ name;
            FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.MakeDirectory);
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                result = true;
                response.Close();
            }
            return result;
        }

        /// <summary>
        /// 获取最后修改时间
        /// </summary>
        /// <param name="remoteFile">远程文件名</param>
        /// <returns>返回最后修改时间</returns>
        public DateTime GetLastWriteTime(string remoteFile)
        {
            string url = Host.TrimEnd('/') + RemotePath + remoteFile;
            FtpWebRequest request = CreateRequest(url, WebRequestMethods.Ftp.GetDateTimestamp);
            DateTime dt;
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                dt = response.LastModified;
                response.Close();
            }
            return dt;
        }

        /// <summary>
        /// 获取FTP服务根目录文件列表
        /// </summary>
        /// <returns>返回文件详细信息列表</returns>
        public List<FTPFileInfo> GetFileInfoList()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 获取FTP文件列表
        /// </summary>
        /// <param name="remotePath"></param>
        /// <returns>返回文件详细信息列表</returns>
        public List<FTPFileInfo> GetFileInfoList(string remotePath)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 获取固定后缀的FTP文件列表
        /// </summary>
        /// <param name="remotePath">远程文件夹路径</param>
        /// <param name="fileSuffix">文件后缀</param>
        /// <returns>返回文件详细信息列表</returns>
        public List<FTPFileInfo> GetFileInfoList(string remotePath, string fileSuffix)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 深层遍历文件夹，获取FTP文件列表
        /// </summary>
        /// <param name="remotePath">远程文件夹路径</param>
        /// <param name="fileSuffix">文件后缀</param>
        /// <param name="deep">遍历获取</param>
        /// /// <param name="isDisConnect">是否关闭连接</param>
        /// <returns>返回文件详细信息列表</returns>
        public List<FTPFileInfo> GetFileInfoList(string remotePath, string fileSuffix, bool deep,bool isDisConnect = true)
        {
            throw new NotImplementedException();
        }

        public bool Download(List<FTPFileInfo> remoteFile, string localPath)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
